/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
* SetUtil.java
*
* Created on November 23, 2007, 12:05 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
 */
package javautil.collections;

import javautil.lang.RandomUtil;
import javautil.swing.components.Selectable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author stewari1
 */
public class SetUtil {

    /** Creates a new instance of SetUtil */
    public SetUtil() {}

    /**
     * useful in updation of an element in a collection
     */
    public static <E> E extractMatch(Collection<E> set, E e) {

        for (Iterator<E> itr = set.iterator(); itr.hasNext(); ) {

            E obj = itr.next();

            if (e.equals(obj)) {
                return obj;
            }
        }

        return null;
    }

    public static <K, V> Set<V> getValues(Map<K, V> map, Set<K> keys) {

        Set<V> values = new HashSet<>();

        for (K key : keys) {

            values.add(map.get(key));
        }

        return values;
    }

    public static <T extends Selectable> Collection<T> getSelectedCollection(
            Collection<T> collection) {


        // since we don`t know the collection implementation it`s safe to use a List than Set (to not delete any duplicate)
        Collection<T> selectedCollection = new ArrayList<>();
        Iterator<T> itr                  = collection.iterator();

        while (itr.hasNext()) {

            T t = itr.next();

            if (t.isSelected()) {
                selectedCollection.add(t);
            }
        }

        return selectedCollection;
    }

    /**
     * Find objects in subset from  superSet keeping their order
     * in  superSet.
     * @param superSet
     * @param subset
     * @return
     */
    public static LinkedHashSet getSubSeq(LinkedHashSet superSet, Set subset) {

        LinkedHashSet resultSet = (LinkedHashSet) superSet.clone();

        for (Iterator itr = resultSet.iterator(); itr.hasNext(); ) {

            if (!subset.contains(itr.next())) {
                itr.remove();
            }
        }

        if (resultSet.size() != subset.size()) {

            System.out.println("Error in getSubSeq():"
                               + "element is not present in the super set. ");
            System.out.println("Super Set: ");
            System.out.println(superSet);
            System.out.println("Sub Set: ");
            System.out.println(subset);
        }

        return resultSet;
    }

    /**
     * @example embedTo : "Ram","Gopal","Toton","Bapon","Anupam"
     *         embedFrom: "Anupam","Gopal","Bapon","Susanta"
     *    return:   "Ram","Anupam","Toton","Gopal","Bapon"
     *
     * @param embedTo description
     * @param embedFrom description
     *
     * @return description
     */
    private static LinkedHashSet getEmbeddedSeq(LinkedHashSet embedTo, LinkedHashSet embedFrom) {


        // find all the elements of embedTo common in embedFrom and get a subSeq from embedFrom
        LinkedHashSet subseqSet = getSubSeq(embedFrom, getCommonSet(embedTo, embedFrom));


        // iterate(by array)embedTo, find objects matching in sbseq and replacing them sequentially
        Object[] embedToArray = embedTo.toArray(new Object[0]);
        Object[] subseqArray  = subseqSet.toArray(new Object[0]);
        int subseqIndex       = 0;

        for (int embedToIndex = 0; embedToIndex < embedToArray.length; embedToIndex++) {

            if (subseqSet.contains(embedToArray[embedToIndex])) {

                embedToArray[embedToIndex] = subseqArray[subseqIndex];

                subseqIndex++;
            }
        }

        return getOrderedSet(embedToArray);
    }

    public static LinkedHashSet getEmbeddedSeq(Object[] embedTo, Object[] embedFrom) {
        return getEmbeddedSeq(getOrderedSet(embedTo), getOrderedSet(embedFrom));
    }

    public static int[] getEmbeddedSeq(int[] embedTo, int[] embedFrom) {

        LinkedHashSet result = getEmbeddedSeq(getOrderedSet(embedTo), getOrderedSet(embedFrom));
        int[] embeddedSeq    = new int[result.size()];
        Iterator itr         = result.iterator();

        for (int i = 0; i < result.size(); i++) {

            embeddedSeq[i] = ((Integer) itr.next()).intValue();
        }

        return embeddedSeq;
    }

    public static Set getCommonSet(Set set1, Set set2) {

        HashSet resultSet = new HashSet();
        Iterator itr1     = set1.iterator();

        while (itr1.hasNext()) {

            Object obj1   = itr1.next();
            Iterator itr2 = set2.iterator();

            while (itr2.hasNext()) {

                Object obj2 = itr2.next();

                if (obj1.equals(obj2)) {

                    resultSet.add(obj1);

                    break;
                }
            }
        }

        return resultSet;
    }

    /**
     * @example int[] superArray = {1,2,3,4,5}
     *         int[] subArray = {3,2,1}
     *       result: 1,2,3
     */
    public static int[] getSubSeq(int[] superArray, int[] subArray) {

        LinkedHashSet resultSet = getSubSeq(getOrderedSet(superArray), getOrderedSet(subArray));
        Integer[] intObjs       = (Integer[]) resultSet.toArray(new Integer[0]);

        return ArrayUtil.getIntegerArray(intObjs);
    }

    /**
     * Method description
     *
     * @param order description
     *
     * @return description
     */
    private static LinkedHashSet getOrderedSet(int[] order) {

        LinkedHashSet resultSet = new LinkedHashSet();

        for (int i : order) {

            resultSet.add(new Integer(i));
        }

        return resultSet;
    }

    /**
     * Method description
     *
     * @param order description
     *
     * @return description
     */
    private static LinkedHashSet getOrderedSet(Object[] order) {

        LinkedHashSet resultSet = new LinkedHashSet();

        for (Object obj : order) {

            resultSet.add(obj);
        }

        return resultSet;
    }

    /**
     * Method description
     */
    private static void test_getOrderedSet() {

        System.out.println(
            getOrderedSet(
                ArrayUtil.getIntegerArray(
                    RandomUtil.shuffle(ArrayUtil.IntegerSequence(10, false)))));
    }

    /**
     * Method description
     */
    private static void test_getEmbeddedSeq() {

        int[] embedTo   = { 1, 2, 3, 4 };
        int[] embedFrom = { 3, 2, 1, 5 };

        System.out.println(getOrderedSet(getEmbeddedSeq(embedTo, embedFrom)));
    }

    public static <T> Set<T> createHashSet(T[] array) {

        Set<T> set = new HashSet<>(array.length);

        for (T t : array) {

            set.add(t);
        }

        return set;
    }

    public static void main(String[] args) {
        test_getEmbeddedSeq();
    }
}
